'''
Description: 
Version: 1.0.1
Autor: hrlu.cn
Date: 2022-03-20 16:19:35
LastEditors: hrlu.cn
LastEditTime: 2022-10-12 15:41:44
'''
import json
import datetime
import itertools

from django.db.models import Q, F, Max
from django.db.models import BooleanField

from processor.models import Logger
from processor.utils import get_quarter_periods


def search_query(param, fields):
    query = Q()
        
    for field in fields:
        query |= Q(**{f'{field}__contains': param})
        
    return query


def search_queryset(queryset, request, fields=[]):
    search = request.GET.get('search', '').replace('｜', '|').replace('～', '~')
    if not search:
        return queryset

    filters = search.split('|')
    result = queryset.none()

    for params in filters:
        group = queryset 
        for param in params.split('&'):
            if param.startswith('~'):
                group = group.exclude(search_query(param.replace('~', ''), fields))
            else:
                group = group.filter(search_query(param, fields))
            
        result = result | group

    return result


def filter_queryset(queryset, request, fields=[], defaults={}):
    conditions = {**defaults}

    for field in fields:
        if request.GET.get(field):
            conditions[field] = request.GET.get(field)
        
            declaration = queryset.model._meta.get_field(field)

            # boolean field filter formater
            if isinstance(declaration, BooleanField):
                conditions[field] = conditions[field] in ('1', 'true')

    return queryset.filter(**conditions)


def paginate_queryset(queryset, request):
    if request.GET.get('all') == 'true':
        return queryset

    try:
        offset = int(request.GET.get('offset', '0'))
        limit = int(request.GET.get('limit', '10'))
    except (TypeError, ValueError):
        offset, limit = 0, 10

    return queryset[offset: offset + limit]


def order_queryset(queryset, request):
    sort = request.GET.get('sort', 'id')
    order = request.GET.get('order', 'asc')

    if not sort:
        return queryset

    if order != 'asc':
        sort = '-' + sort

    return queryset.order_by(sort)


def rest_queryset(queryset, request, filter_fields=[], search_fields=[]):
    # total_not_filtered = queryset.count()

    queryset = filter_queryset(queryset, request, filter_fields)
    queryset = search_queryset(queryset, request, search_fields)
    queryset = order_queryset(queryset, request)
    total = queryset.count()

    queryset = paginate_queryset(queryset, request)

    if queryset.query.default_cols:
        queryset = queryset.values()

    return {
        # 'totalNotFiltered': total_not_filtered,
        'total': total,
        'rows': list(queryset),
    }


def achievement_filter(queryset, request):
    filter = request.GET.get('advance_filter', '{}')
    try:
        filter = json.loads(filter)
        assert isinstance(filter, dict)
    except:
        return queryset

    variables = filter.get('variables', [])
    conditions = filter.get('conditions', [])

    if not conditions:
        return queryset

    indicators = {
        'ROE': 'roe',
        'TR': 'revenue',
        'TC': 'total_cogs',
        'PD': 'profit_dedt',
        'PTG': 'profit_to_gr',
        'AT': 'assets_turn',
        'STG': 'saleexp_to_gr',
        'PCH': 'per_capita_hold'
    }
    periods = get_quarter_periods()

    defined = [f'{i}{p}' for p in periods for i, d in indicators.items()]
    register_indicators = []

    def register_indicator(tag, func=True):
        if tag in defined:
            nonlocal register_indicators
            if tag not in register_indicators:
                register_indicators.append(tag)
                for (i, d), p in itertools.product(indicators.items(), periods):
                    if f'{i}{p}' == tag:
                        nonlocal queryset
                        queryset = queryset.annotate(
                            **{f'{i}{p}': Max(d, filter=Q(quarter=p))}
                        )
            return F(tag) if func else tag
        else:
            return float(tag)        
    
    for var in variables:
        try:
            arg1, arg2 = var['arg1'], var['arg2']
            param, opt = var['param'], var['opt']

            arg1 = register_indicator(arg1)
            arg2 = register_indicator(arg2)

            if opt == 'add':
                exp = arg1 + arg2
            elif opt == 'sub':
                exp = arg1 - arg2
            elif opt == 'mult':
                exp = arg1 * arg2
            elif opt == 'div':
                exp = arg1 / arg1

            queryset = queryset.annotate(
                **{param: exp}
            )

            register_indicators.append(param)
            defined.append(param)
        except Exception as e:
            Logger.warning(msg=f"定义变量 {param} = {arg1} {opt} {arg2} 解析失败：{e}", module='monitor')
    
    chosen = queryset.none()

    conditions_groups = []
    for condition in conditions:
        if condition['logic'] == 'or':
            conditions_groups.append([])
        conditions_groups[-1].append(condition)

    for conditions in conditions_groups:
        try:
            for cond in conditions:
                arg1, arg2 = cond['arg1'], cond['arg2']
                logic, opt = cond['logic'], cond['opt']

                arg1 = register_indicator(arg1, func=False)
                arg2 = register_indicator(arg2)
            
            for cond in conditions:
                arg1, arg2 = cond['arg1'], cond['arg2']
                logic, opt = cond['logic'], cond['opt']
                
                querygroup = queryset.filter(
                    **{f'{arg1}__{opt}': arg2}
                )
            chosen |= querygroup
        
        except Exception as e:
            Logger.warning(msg=f"筛选条件 {conditions} 解析失败：{e}", module='monitor')
    
    stock_basics = chosen.values_list('stock_basic', flat=True).distinct()
    
    res = queryset.filter(stock_basic__in=stock_basics)
    return res